//
// Created by hrg on 2019/11/23.
//

#ifndef RESTRUCTURE_SLAVE_H
#define RESTRUCTURE_SLAVE_H

#include <eeprom.h>
#include <ecxml.h>
#include <map>
#include <ecrt.h>
#include <memory>

class Slave{
public:
    Slave();

    Slave(ec_master_t* master, int index);

    ~Slave();
    static EcType Type(EcXml& ecxml, ec_master_t* master, int index);
    static bool Match(EcXml& ecxml, ec_master_t* master, int index);
    virtual bool Init(ec_master_t* master, int index);
    virtual bool Parse(EcXml &ecxml, ec_master_t* master, int index);
    virtual bool Config(ec_domain_t* domain);

    /**
     * @Func
     *      SetType
     * @Berif
     *      set slave type
     * @Input
     *      type: slave type
     * @Output
     *      none
     */
    void SetType(EcType type);

    EcType GetType();

    /**
     * @Func
     *      SetDC
     * @Berif
     *      set current slave DC
     * @Input
     *      none
     * @Output
     *      none
     */
    void SetDC(uint32_t cycle1, int32_t shift1, uint32_t cycle2, int32_t shift2);

    /**
     * @Func
     *      SetMode
     * @Berif
     *      set current slave mode
     * @Input
     *      mode: operation mode
     * @Output
     *      none
     */
    void SetMode(int8_t mode);

    /**
     * @Func
     *      SetPos
     * @Berif
     *      set target position
     * @Input
     *      targetpos: the position of targetpos
     * @Output
     *      none
     */
    virtual void SetPos(int32_t targetPos);

    /**
     * @Func
     *      SetEnable
     * @Berif
     *      start slave
     * @Input
     *      enable
     * @Output
     *      none
     */
    void SetEnable(bool enable);

    bool GetEnable();

    /**
     * @Func
     *      StatusTransite
     * @Berif
     *      slave status transite
     * @Input
     *      none
     * @Output
     *      none
     */
    virtual bool StatusTransite();

    virtual uint16_t GetErrorCode();

    /**
     * @Func
     *      Retrieve
     * @Berif
     *      Read the feedback from the slave
     * @Input
     *      domainPtr: the ethercat frame of data domain
     * @Output
     *      none
     */
    virtual void Retrieve(uint8_t* domainPtr);

    /**
     * @Func
     *      Publisher
     * @Berif
     *      Write data to be distributed to slave
     * @Input
     *      domainPtr: the ethercat frame of data domain
     * @Output
     *      none
     */
    virtual void Publisher(uint8_t* domainPtr);

    /**
     * @Func
     *      GetInitPos
     * @Berif
     *      Get the init location of the slave
     * @Input
     *      initpos: return values
     * @Output
     *      return true, initpos is valid;
     *      return false, initpos is invalid;
     */
    virtual bool GetInitPos(int& initpos) = delete;

    /**
     * @Func
     *      GetState
     * @Berif
     *      Get the state of the slave
     * @Input
     *      none
     * @Output
     *      if comm success, return 0x2
     *      if op success, return 0x4
     *      or other state , return 0x1
     */
    virtual int8_t GetState();

    /**
     * @Func
     *      GetConfig
     * @Berif
     *      Get the config of the slave
     * @Input
     *      none
     * @Output
     *      return the config of the slave
     */
    ec_slave_config_t* GetConfig();
    
    /**
     * @Func
     *      SetPdoEntry
     * @Berif
     *      according sync manage to product pdo entry
     * @Input
     *      syncInfo: the sync manage of slave
     * @Output
     *      success, return true; or, return false
     */
    virtual bool SetPdoEntry(ec_sync_info_t *syncInfo);

    /**
     * @Func
     *      Reset
     * @Berif
     *      设置当前从站复位
     * @Input
     *      none
     * @Output
     *      none
    */
    virtual void Reset();

    int GetActualPos() { return txpdo_.pos; }
    int GetTargetPos() { return rxpdo_.pos; }

    int GetActualVel() { return txpdo_.vel; }
    int GetTargetVel() { return rxpdo_.vel; }
    
    void printPdoEntry();
public:
    EcType m_SlaveType_;

protected:
    struct RxPdo{
        int8_t mode;
        uint16_t control;

        int32_t pos = 0;
        int32_t vel = 0;
        int32_t tor = 0;

        uint32_t maxvel;
    };

    struct TxPdo{
        uint16_t status = 0;
        uint16_t errcode = 0;

        int32_t pos = 0;
        int32_t vel = 0;
        int32_t tor = 0;

        int32_t dig = 0;                    // 数字量
        std::map<int32_t, int16_t> ana;     // 模拟量
    };
protected:
    ec_slave_config_t *m_SlvConfig_;
    ec_master_t* m_Master_;

    ec_slave_info_t m_SlvInfo_;
    ec_sync_info_t *m_SyncInfo_;
    ec_pdo_entry_reg_t *m_PdoEntry_;
    ec_sdo_request_t *sdo_;

    EEprom *m_EEprom_;
    uint32_t  erroroffset_;
    uint32_t *m_PdoBitPos_;
    uint32_t *m_PdoEntryOffset_;
    std::map<uint16_t, uint32_t*> m_PdoOffset_;    // PDO offset

    static uint16_t alias_;
    static uint16_t realpos_;

    RxPdo rxpdo_;
    TxPdo txpdo_;

    int m_Index_;
    int delay_;

    bool enable_; // 是否需要使能
    bool m_Used_;   // 是否可用
    bool reset_;  // 是否复位当前从站
    int m_PdoNum_;
};
typedef std::shared_ptr<Slave> SlavePtr;

#endif //RESTRUCTURE_SLAVE_H
